#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
THN TELEPORT version 2

Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

This library is dual-licensed.
  + For commercial software, please obtain a commercial license from THN Solutions LLC.

  + For free software, this library is licensed under GPL version 3. A summary of GPLv3 is
    listed below. You should also find a copy of GPLv3 as file License_GPLv3.txt included
	with the source-code files.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 3 as published by
    the Free Software Foundation  of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 28-Dec-09 | HaiNM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

using Thn;
using Thn.IO;
using Thn.Communication;
using Thn.Application.Configuration;
using Thn.Communication.Teleporting.Settings;
#endregion

namespace Thn.Communication.Teleporting.Connectors.WebRequest
{
    /// <summary>
    /// A client-side connector for Teleport using <see cref="HttpWebRequest"/>.
    /// </summary>
#if DEBUG
    public
#else
    public
#endif
 class ClientConnector : ITeleportConnector
    {
        #region Fields
        //System.Net.WebRequest mRequest = null;
        //StreamContextReader mReader = null;
        //StreamContextWriter mWriter = null;

        #endregion

        #region Properties

        #region Url
        private string mUrl;
        /// <summary>
        /// Gets/Sets the URL for connecting to the server.
        /// </summary>
        public string Url
        {
            get { return mUrl; }
            set { mUrl = value; }
        }
        #endregion

        #region Ssl Validatation
        private SslValidation mSslValidation = SslValidation.Default;
        /// <summary>
        /// Gets/Sets validation mode for SSL certificates.
        /// </summary>
        public SslValidation SslValidation
        {
            get { return mSslValidation; }
            set
            {
                mSslValidation = value;
                ApplySslValidation();
            }
        }
        #endregion
        
        #endregion

        #region Apply Configuration
        /// <summary>
        /// Read parameters from configuration.
        /// </summary>
        void ApplyConfiguration(ConfigSection section)
        {
            //Url
            string tmp = section["Url"];
            if ((tmp != null) && (tmp != string.Empty)) mUrl = tmp;

            //trace
            tmp = section["TRACE"];
            if ((tmp != null) && (tmp != string.Empty) && (tmp.ToUpper() == "TRACE")) TeleportSettings.Tracing = true;

            //SslValidation
            tmp = section["SslValidation"];
            if ((tmp != null) && (tmp != string.Empty))
            {
                tmp = tmp.ToUpper();
                if (tmp == "ACCEPTALL")
                {
                    this.SslValidation = SslValidation.AcceptAll;
                }
                else if (tmp == "CUSTOM")
                {
                    this.SslValidation = SslValidation.Custom;
                }
            }            
        }
        #endregion

        #region SSL Validation

        #region Apply Ssl Validation
        void ApplySslValidation()
        {
            if (mSslValidation == SslValidation.AcceptAll) AcceptAllSslCertificate();
            else if (mSslValidation == SslValidation.Custom) AcceptCustomSslCertificate();
            else AcceptValidSslCertificate();
        }
        #endregion

        #region Accept All Ssl Certificate
        void AcceptAllSslCertificate()
        {
            //setup SSL certificate validator
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(AcceptAllRemoteCertificateCallback);
        }

        // callback used to validate the certificate in an SSL conversation
        private bool AcceptAllRemoteCertificateCallback(
        object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors policyErrors
        )
        {
            return mSslValidation == SslValidation.AcceptAll;
        }
        #endregion

        #region Accept Valid Ssl Certificate
        void AcceptValidSslCertificate()
        {
            //setup SSL certificate validator
            ServicePointManager.ServerCertificateValidationCallback = null;
        }
        #endregion

        #region Accept Custom Ssl Certificate
        void AcceptCustomSslCertificate()
        {
            mSslValidation = SslValidation.Custom;
            throw new NotImplementedException();
        }
        #endregion

        #endregion

        #region ITeleportConnector Members

        #region Teleporter
        private ITeleporter mTeleporter;
        /// <summary>
        /// Gets/Sets the teleporter this connector shall be connecting for.
        /// <para>When unassigned, the connector shall use the default teleporter.</para>
        /// </summary>
        public ITeleporter Teleporter
        {
            get
            {
                ITeleporter result = null;
                if (mTeleporter != null) result = mTeleporter;
                else result = Teleport.Teleporter;

                return result;
            }
            set { mTeleporter = value; }
        }
        #endregion

        #region Configuration
        private ConfigSection mConfiguration;
        /// <summary>
        /// Gets/Sets a custom configuration for initialization.
        /// </summary>
        public ConfigSection Configuration
        {
            get { return mConfiguration; }
            set
            {
                mConfiguration = value;
                if (mConfiguration != null) ApplyConfiguration(mConfiguration);
                else Thn.Exceptions.NullArgumentException.Publish("Configuration");
            }
        }
        #endregion

        #region Initialize
        /// <summary>
        /// Prepare this connector.
        /// </summary>
        public void Initialize()
        {
            //Get teleporter object
            Teleporter tp = (Teleporter)this.Teleporter;

            //Intercept send message event to process by sending the message to remoting
            tp.SendMessage += new TeleportRequestHandler(tp_SendMessage);            
        }
        #endregion        

        #endregion

        #region Teleport - Send message
        /// <summary>
        /// Intercept send message event to process by sending the message to remoting
        /// </summary>
        string tp_SendMessage(string gateway, string method, string encodedArgs)
        {
            Exception error = null;
            string result = string.Empty;
            Stream content = null;
            System.Net.WebRequest mRequest = null;
            StreamContextReader mReader = null;
            StreamContextWriter mWriter = null;

            #region Build request
            mRequest = System.Net.WebRequest.Create(mUrl);
            //mRequest.Method = "PUT";
            mRequest.Method = "POST";

            content = mRequest.GetRequestStream();
            if (mWriter != null) mWriter.Target = content;
            else mWriter = new StreamContextWriter(content);

            //write contents
            mWriter.Write(gateway);
            mWriter.Write(method);
            mWriter.Write(encodedArgs);

            //clean up            
            content.Close();
            #endregion

            #region Send Request
            HttpWebResponse response = null;

            try
            {
                response = mRequest.GetResponse() as HttpWebResponse;
            }
            catch (Exception ex)
            {
                error = ex;
            }
            #endregion

            #region Read response

            if ((response != null) && (response.StatusCode == HttpStatusCode.OK))
            {
                //Log.Information("Status - Code: {0} Description: {1}", response.StatusCode, response.StatusDescription);

                content = response.GetResponseStream();
                if (mReader != null) mReader.Source = content;
                else mReader = new StreamContextReader(content);

                //read content
                result = mReader.ReadString();

                //Log.Information("Result: {0}", result);

                //clean up
                content.Close();
            }
            else if (response != null)
            {
                //int tmp = 0;
                throw new NotSupportedException(string.Format("Unexpected response code : {0}", response.StatusCode));
            }
            #endregion

            //rethrow error
            if (error != null) throw error;
            #region Defensive tracing
            else
            {
                //no error, proceed normally
            }
            #endregion

            return result;
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public ClientConnector()
        { }
        #endregion
    }
}
